{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1fe4c9e0-8404-42b6-ae21-0c3685166999",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "773461d0-ee4d-4d50-b09e-51873a46732f",
   "metadata": {},
   "source": [
    "- https://langchain-ai.github.io/langgraph/concepts/\n",
    "- https://langchain-ai.github.io/langgraph/how-tos/graph-api/#create-branches\n",
    "    - https://langchain-ai.github.io/langgraphjs/how-tos/branching/\n",
    "- langgraph\n",
    "    - 很灵活，很强大；\n",
    "- 经典的 agent workflow 也没有那么多: https://www.anthropic.com/engineering/building-effective-agents\n",
    "    - 很多都可以作为系统设计的原型（prototype）进行拓展；"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57f8a9c3-d689-44fc-ba75-399c7ec93b27",
   "metadata": {},
   "source": [
    "### fan-in fan-out"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f4dff8e-094c-42ca-acf7-a6c05c1ef720",
   "metadata": {},
   "source": [
    "- LangGraph natively supports fan-out and fan-in using either regular edges or conditionalEdges.\n",
    "- **Parallel execution of nodes** (web-search) is essential to speed up overall graph operation. LangGraph offers **native support** for parallel execution of nodes, which can significantly enhance the performance of **graph-based workflows**. This parallelization is achieved through fan-out and fan-in mechanisms, utilizing both standard edges and conditional_edges. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6406b940-ed8b-4fa2-b8b0-2c895f810d44",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"400\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='', width=400)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "895895ba-b8f4-4784-a87a-6287afbea02f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "from typing import Annotated, Any\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    # The operator.add reducer fn makes this append-only\n",
    "    aggregate: Annotated[list, operator.add]\n",
    "\n",
    "\n",
    "def a(state: State):\n",
    "    print(f'Adding \"A\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"A\"]}\n",
    "\n",
    "\n",
    "def b(state: State):\n",
    "    print(f'Adding \"B\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"B\"]}\n",
    "\n",
    "\n",
    "def c(state: State):\n",
    "    print(f'Adding \"C\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"C\"]}\n",
    "\n",
    "\n",
    "def d(state: State):\n",
    "    print(f'Adding \"D\" to {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"D\"]}\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(a)\n",
    "builder.add_node(b)\n",
    "builder.add_node(c)\n",
    "builder.add_node(d)\n",
    "\n",
    "builder.add_edge(START, \"a\")\n",
    "\n",
    "# nodes \"b\" and \"c\" are executed concurrently \n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "af24c29f-418d-4951-a022-32d07b776439",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "18f7ae97-8315-420b-b34c-c2443e139c59",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding \"A\" to []\n",
      "Adding \"B\" to ['A']\n",
      "Adding \"C\" to ['A']\n",
      "Adding \"D\" to ['A', 'B', 'C']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'aggregate': ['A', 'B', 'C', 'D']}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"aggregate\": []}, \n",
    "             {\"configurable\": {\"thread_id\": \"foo\"}})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d07e500-5cfe-47dd-98e6-af38e6cec8e8",
   "metadata": {},
   "source": [
    "### Map-reduce and the Send API"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c20ac1e-ae13-4964-b405-1988eeaf6951",
   "metadata": {},
   "source": [
    "- map: `[Send(xx) for xx in list]`\n",
    "- reduce: `jokes: Annotated[list, operator.add]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f00dae4c-0411-406a-8116-93c1ab766504",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.types import Send\n",
    "from langgraph.graph import END, StateGraph, START\n",
    "\n",
    "\n",
    "# This will be the overall state of the main graph.\n",
    "# It will contain a topic (which we expect the user to provide)\n",
    "# and then will generate a list of subjects, and then a joke for\n",
    "# each subject\n",
    "class OverallState(TypedDict):\n",
    "    topic: str\n",
    "    subjects: list\n",
    "    # Notice here we use the operator.add\n",
    "    # This is because we want combine all the jokes we generate\n",
    "    # from individual nodes back into one list - this is essentially\n",
    "    # the \"reduce\" part\n",
    "    jokes: Annotated[list, operator.add]\n",
    "    best_selected_joke: str\n",
    "\n",
    "\n",
    "# This will be the state of the node that we will \"map\" all\n",
    "# subjects to in order to generate a joke\n",
    "class JokeState(TypedDict):\n",
    "    subject: str\n",
    "\n",
    "\n",
    "# This is the function we will use to generate the subjects of the jokes.\n",
    "# In general the length of the list generated by this node could vary each run.\n",
    "def generate_topics(state: OverallState):\n",
    "    # Simulate a LLM.\n",
    "    return {\"subjects\": [\"lions\", \"elephants\", \"penguins\"]}\n",
    "\n",
    "\n",
    "# Here we generate a joke, given a subject\n",
    "def generate_joke(state: JokeState):\n",
    "    # Simulate a LLM.\n",
    "    joke_map = {\n",
    "        \"lions\": \"Why don't lions like fast food? Because they can't catch it!\",\n",
    "        \"elephants\": \"Why don't elephants use computers? They're afraid of the mouse!\",\n",
    "        \"penguins\": (\n",
    "            \"Why don’t penguins like talking to strangers at parties? \"\n",
    "            \"Because they find it hard to break the ice.\"\n",
    "        ),\n",
    "    }\n",
    "    return {\"jokes\": [joke_map[state[\"subject\"]]]}\n",
    "\n",
    "\n",
    "# Here we define the logic to map out over the generated subjects\n",
    "# We will use this as an edge in the graph\n",
    "def continue_to_jokes(state: OverallState):\n",
    "    # We will return a list of `Send` objects\n",
    "    # Each `Send` object consists of the name of a node in the graph\n",
    "    # as well as the state to send to that node\n",
    "    return [Send(\"generate_joke\", {\"subject\": s}) for s in state[\"subjects\"]]\n",
    "\n",
    "\n",
    "# Here we will judge the best joke\n",
    "def best_joke(state: OverallState):\n",
    "    return {\"best_selected_joke\": \"penguins\"}\n",
    "\n",
    "\n",
    "# Construct the graph: here we put everything together to construct our graph\n",
    "builder = StateGraph(OverallState)\n",
    "builder.add_node(\"generate_topics\", generate_topics)\n",
    "builder.add_node(\"generate_joke\", generate_joke)\n",
    "builder.add_node(\"best_joke\", best_joke)\n",
    "builder.add_edge(START, \"generate_topics\")\n",
    "builder.add_conditional_edges(\"generate_topics\", continue_to_jokes, [\"generate_joke\"])\n",
    "builder.add_edge(\"generate_joke\", \"best_joke\")\n",
    "builder.add_edge(\"best_joke\", END)\n",
    "graph = builder.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8b736a8e-ad24-4316-bff7-0c089dc4f409",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e3fb0e2e-bb5f-4e73-b0d0-b4c4138660d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'generate_topics': {'subjects': ['lions', 'elephants', 'penguins']}}\n",
      "{'generate_joke': {'jokes': [\"Why don't lions like fast food? Because they can't catch it!\"]}}\n",
      "{'generate_joke': {'jokes': [\"Why don't elephants use computers? They're afraid of the mouse!\"]}}\n",
      "{'generate_joke': {'jokes': ['Why don’t penguins like talking to strangers at parties? Because they find it hard to break the ice.']}}\n",
      "{'best_joke': {'best_selected_joke': 'penguins'}}\n"
     ]
    }
   ],
   "source": [
    "# Call the graph: here we call it to generate a list of jokes\n",
    "for step in graph.stream({\"topic\": \"animals\"}):\n",
    "    print(step)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "277d08fd-d085-4ce3-8d5b-d58a596d8f31",
   "metadata": {},
   "source": [
    "### create and control loops"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7322a96f-23c6-4721-a727-2f2b5f304011",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "from typing import Annotated, Literal\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    # The operator.add reducer fn makes this append-only\n",
    "    aggregate: Annotated[list, operator.add]\n",
    "\n",
    "\n",
    "def a(state: State):\n",
    "    print(f'Node A sees {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"A\"]}\n",
    "\n",
    "\n",
    "def b(state: State):\n",
    "    print(f'Node B sees {state[\"aggregate\"]}')\n",
    "    return {\"aggregate\": [\"B\"]}\n",
    "\n",
    "\n",
    "# Define nodes\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(a)\n",
    "builder.add_node(b)\n",
    "\n",
    "\n",
    "# Define edges\n",
    "def route(state: State) -> Literal[\"b\", END]:\n",
    "    if len(state[\"aggregate\"]) < 7:\n",
    "        return \"b\"\n",
    "    else:\n",
    "        return END\n",
    "\n",
    "\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_conditional_edges(\"a\", route)\n",
    "builder.add_edge(\"b\", \"a\")\n",
    "graph = builder.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4c27c6ab-e069-4045-acc2-2ee9e361d490",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24607142-671f-49d5-bd95-d1abc96388dd",
   "metadata": {},
   "source": [
    "- This architecture is similar to a ReAct agent in which node\n",
    "    - \"a\" is a tool-calling model,\n",
    "    - and node \"b\" represents the tools."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5b0d41db-b953-4a82-8572-e60c6c05cc47",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node A sees []\n",
      "Node B sees ['A']\n",
      "Node A sees ['A', 'B']\n",
      "Node B sees ['A', 'B', 'A']\n",
      "Node A sees ['A', 'B', 'A', 'B']\n",
      "Node B sees ['A', 'B', 'A', 'B', 'A']\n",
      "Node A sees ['A', 'B', 'A', 'B', 'A', 'B']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'aggregate': ['A', 'B', 'A', 'B', 'A', 'B', 'A']}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"aggregate\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ad3f79d-3ab2-4236-b388-9ff04c0a8396",
   "metadata": {},
   "source": [
    "### conditional edges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40b77236-b7ac-4ce1-b553-65fcbbe0c1cc",
   "metadata": {},
   "source": [
    "- `builder.add_conditional_edges(\"generate_topics\", continue_to_jokes, [\"generate_joke\"])`\n",
    "    - path_map 参数的目的是告诉 langgraph，从 source 节点出发，经过 path 函数决策后，所有可能的下一个节点有哪些。\n",
    "        - 当它是一个字典时：它用于将 path 函数返回的简单值（如字符串 \"A\"）映射到实际的节点名（如 \"node_A\"）。\n",
    "        - 当它是一个列表时 (我们当前的情况)：它直接声明了所有合法的目标节点名称。langgraph 用这个列表来验证 path 函数返回的目标是否有效，并帮助构建和可视化计算图。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
